package com.caissa.framework.starter.autoconfigure.reference;

import com.alibaba.druid.filter.FilterAdapter;
import com.alibaba.druid.filter.FilterChain;
import com.alibaba.druid.proxy.jdbc.DataSourceProxy;
import com.alibaba.druid.proxy.jdbc.JdbcParameter;
import com.alibaba.druid.proxy.jdbc.PreparedStatementProxy;
import com.alibaba.druid.proxy.jdbc.ResultSetProxy;
import com.alibaba.druid.proxy.jdbc.StatementProxy;
import com.dianping.cat.Cat;
import com.dianping.cat.CatConstants;
import com.dianping.cat.message.Message;
import com.dianping.cat.message.Transaction;

import java.sql.SQLException;
import java.util.Map;
import java.util.Objects;
import java.util.function.BiConsumer;

/**
 *
 * cat监控drui连接池
 *
 * @auther wangwei
 * @date 2020/11/4
 */
public class DruidCatFilter extends FilterAdapter {

    private String dbType;

    private String url;


    @Override
    public void init(DataSourceProxy dataSource) {
        //dbType = dataSource.getDbType();
        dbType = "mysql";
        url = dataSource.getUrl();
    }

    @Override
    public boolean statement_execute(FilterChain chain, StatementProxy statement, String sql) throws SQLException {
        String sqlCharacter = getSqlCharacter(sql);

        Transaction transaction = Cat.newTransaction(CatConstants.TYPE_SQL, getSqlCharacter(sql));
        Cat.logEvent("SQL.DbType", dbType);
        Cat.logEvent("SQL.Database", url);
        Cat.logEvent("SQL.param", getParams(statement.getParameters()));
        //Cat.logEvent("SQL.sql", getShowSql(sqlCharacter,getParams(statement.getParameters())));
        boolean result;
        try {
            result = super.statement_execute(chain, statement, sql);
            transaction.setStatus(Message.SUCCESS);
        } catch (SQLException e) {
            Cat.logError(e);
            transaction.setStatus(e);
            throw e;
        } finally {
            transaction.complete();
        }

        return result;
    }

    @Override
    public boolean statement_execute(FilterChain chain, StatementProxy statement, String sql, int autoGeneratedKeys)
            throws SQLException {
        String sqlCharacter = getSqlCharacter(sql);

        Transaction transaction = Cat.newTransaction(CatConstants.TYPE_SQL, sqlCharacter);
        Cat.logEvent("SQL.DbType", dbType);
        Cat.logEvent("SQL.Database", url);
        Cat.logEvent("SQL.param", getParams(statement.getParameters()));
        //Cat.logEvent("SQL.sql", getShowSql(sqlCharacter,getParams(statement.getParameters())));
        boolean result;
        try {
            result = super.statement_execute(chain, statement, sql, autoGeneratedKeys);
            transaction.setStatus(Message.SUCCESS);
        } catch (SQLException e) {
            Cat.logError(e);
            transaction.setStatus(e);
            throw e;
        } finally {
            transaction.complete();
        }

        return result;
    }

    @Override
    public boolean statement_execute(FilterChain chain, StatementProxy statement, String sql, int[] columnIndexes)
            throws SQLException {
        String sqlCharacter = getSqlCharacter(sql);

        Transaction transaction = Cat.newTransaction(CatConstants.TYPE_SQL, sqlCharacter);
        Cat.logEvent("SQL.DbType", dbType);
        Cat.logEvent("SQL.Database", url);
        Cat.logEvent("SQL.param", getParams(statement.getParameters()));
        //Cat.logEvent("SQL.sql", getShowSql(sqlCharacter,getParams(statement.getParameters())));
        boolean result;
        try {
            result = super.statement_execute(chain, statement, sql, columnIndexes);
            transaction.setStatus(Message.SUCCESS);
        } catch (SQLException e) {
            Cat.logError(e);
            transaction.setStatus(e);
            throw e;
        } finally {
            transaction.complete();
        }

        return result;
    }

    @Override
    public boolean statement_execute(FilterChain chain, StatementProxy statement, String sql, String[] columnNames)
            throws SQLException {
        String sqlCharacter = getSqlCharacter(sql);

        Transaction transaction = Cat.newTransaction(CatConstants.TYPE_SQL, sqlCharacter);
        Cat.logEvent("SQL.DbType", dbType);
        Cat.logEvent("SQL.Database", url);
        Cat.logEvent("SQL.param", getParams(statement.getParameters()));
        //Cat.logEvent("SQL.sql", getShowSql(sqlCharacter,getParams(statement.getParameters())));
        boolean result;
        try {
            result = super.statement_execute(chain, statement, sql, columnNames);
            transaction.setStatus(Message.SUCCESS);
        } catch (SQLException e) {
            Cat.logError(e);
            transaction.setStatus(e);
            throw e;
        } finally {
            transaction.complete();
        }

        return result;
    }

    @Override
    public int[] statement_executeBatch(FilterChain chain, StatementProxy statement) throws SQLException {
        Transaction transaction = Cat.newTransaction(CatConstants.TYPE_SQL, "batchsql");
        Cat.logEvent("SQL.DbType", dbType);
        Cat.logEvent("SQL.Database", url);
        Cat.logEvent("SQL.sql", statement.getBatchSql());//TODO  限制长度
        int[] result;
        try {
            result = super.statement_executeBatch(chain, statement);
            transaction.setStatus(Message.SUCCESS);
        } catch (SQLException e) {
            Cat.logError(e);
            transaction.setStatus(e);
            throw e;
        } finally {
            transaction.complete();
        }

        return result;
    }

    @Override
    public ResultSetProxy statement_executeQuery(FilterChain chain, StatementProxy statement, String sql)
            throws SQLException {
        String sqlCharacter = getSqlCharacter(sql);

        Transaction transaction = Cat.newTransaction(CatConstants.TYPE_SQL, sqlCharacter);
        Cat.logEvent("SQL.DbType", dbType);
        Cat.logEvent("SQL.Database", url);
        Cat.logEvent("SQL.param", getParams(statement.getParameters()));
        //Cat.logEvent("SQL.sql", getShowSql(sqlCharacter,getParams(statement.getParameters())));
        ResultSetProxy result;
        try {
            result = super.statement_executeQuery(chain, statement, sql);
            transaction.setStatus(Message.SUCCESS);
        } catch (SQLException e) {
            Cat.logError(e);
            transaction.setStatus(e);
            throw e;
        } finally {
            transaction.complete();
        }

        return result;
    }

    @Override
    public int statement_executeUpdate(FilterChain chain, StatementProxy statement, String sql) throws SQLException {
        String sqlCharacter = getSqlCharacter(sql);

        Transaction transaction = Cat.newTransaction(CatConstants.TYPE_SQL, sqlCharacter);
        Cat.logEvent("SQL.DbType", dbType);
        Cat.logEvent("SQL.Database", url);
        Cat.logEvent("SQL.param", getParams(statement.getParameters()));
        //Cat.logEvent("SQL.sql", getShowSql(sqlCharacter,getParams(statement.getParameters())));
        int result;
        try {
            result = super.statement_executeUpdate(chain, statement, sql);
            transaction.setStatus(Message.SUCCESS);
        } catch (SQLException e) {
            Cat.logError(e);
            transaction.setStatus(e);
            throw e;
        } finally {
            transaction.complete();
        }
        return result;
    }

    @Override
    public int statement_executeUpdate(FilterChain chain, StatementProxy statement, String sql, int autoGeneratedKeys)
            throws SQLException {
        String sqlCharacter = getSqlCharacter(sql);

        Transaction transaction = Cat.newTransaction(CatConstants.TYPE_SQL, sqlCharacter);
        Cat.logEvent("SQL.DbType", dbType);
        Cat.logEvent("SQL.Database", url);
        Cat.logEvent("SQL.param", getParams(statement.getParameters()));
        //Cat.logEvent("SQL.sql", getShowSql(sqlCharacter,getParams(statement.getParameters())));
        int result;
        try {
            result = super.statement_executeUpdate(chain, statement, sql, autoGeneratedKeys);
            transaction.setStatus(Message.SUCCESS);
        } catch (SQLException e) {
            Cat.logError(e);
            transaction.setStatus(e);
            throw e;
        } finally {
            transaction.complete();
        }
        return result;
    }

    @Override
    public int statement_executeUpdate(FilterChain chain, StatementProxy statement, String sql, int[] columnIndexes)
            throws SQLException {
        String sqlCharacter = getSqlCharacter(sql);

        Transaction transaction = Cat.newTransaction(CatConstants.TYPE_SQL, sqlCharacter);
        Cat.logEvent("SQL.DbType", dbType);
        Cat.logEvent("SQL.Database", url);
        Cat.logEvent("SQL.param", getParams(statement.getParameters()));
        //Cat.logEvent("SQL.sql", getShowSql(sqlCharacter,getParams(statement.getParameters())));
        int result;
        try {
            result = super.statement_executeUpdate(chain, statement, sql, columnIndexes);
            transaction.setStatus(Message.SUCCESS);
        } catch (SQLException e) {
            Cat.logError(e);
            transaction.setStatus(e);
            throw e;
        } finally {
            transaction.complete();
        }
        return result;
    }

    @Override
    public int statement_executeUpdate(FilterChain chain, StatementProxy statement, String sql, String[] columnNames)
            throws SQLException {
        String sqlCharacter = getSqlCharacter(sql);

        Transaction transaction = Cat.newTransaction(CatConstants.TYPE_SQL, sqlCharacter);
        Cat.logEvent("SQL.DbType", dbType);
        Cat.logEvent("SQL.Database", url);
        Cat.logEvent("SQL.param", getParams(statement.getParameters()));
        //Cat.logEvent("SQL.sql", getShowSql(sqlCharacter,getParams(statement.getParameters())));
        int result;
        try {
            result = super.statement_executeUpdate(chain, statement, sql, columnNames);
            transaction.setStatus(Message.SUCCESS);
        } catch (SQLException e) {
            Cat.logError(e);
            transaction.setStatus(e);
            throw e;
        } finally {
            transaction.complete();
        }
        return result;
    }

    @Override
    public boolean preparedStatement_execute(FilterChain chain, PreparedStatementProxy statement) throws SQLException {
        String sqlCharacter = getSqlCharacter(statement.getSql());

        Transaction transaction = Cat.newTransaction(CatConstants.TYPE_SQL, sqlCharacter);
        Cat.logEvent("SQL.DbType", dbType);
        Cat.logEvent("SQL.Database", url);
        Cat.logEvent("SQL.param", getParams(statement.getParameters()));
        //Cat.logEvent("SQL.sql", getShowSql(sqlCharacter,getParams(statement.getParameters())));
        boolean result;
        try {
            result = super.preparedStatement_execute(chain, statement);
            transaction.setStatus(Message.SUCCESS);
        } catch (SQLException e) {
            Cat.logError(e);
            transaction.setStatus(e);
            throw e;
        } finally {
            transaction.complete();
        }
        return result;
    }

    @Override
    public ResultSetProxy preparedStatement_executeQuery(FilterChain chain, PreparedStatementProxy statement)
            throws SQLException {
        String sqlCharacter = getSqlCharacter(statement.getSql());
        Transaction transaction = Cat.newTransaction(CatConstants.TYPE_SQL, sqlCharacter);
        Cat.logEvent("SQL.DbType", dbType);
        Cat.logEvent("SQL.Database", url);
        Cat.logEvent("SQL.param", getParams(statement.getParameters()));
        //Cat.logEvent("SQL.sql", getShowSql(sqlCharacter,getParams(statement.getParameters())));
        ResultSetProxy result;
        try {
            result = super.preparedStatement_executeQuery(chain, statement);
            transaction.setStatus(Message.SUCCESS);
        } catch (SQLException e) {
            Cat.logError(e);
            transaction.setStatus(e);
            throw e;
        } finally {
            transaction.complete();
        }
        return result;
    }


    @Override
    public int preparedStatement_executeUpdate(FilterChain chain, PreparedStatementProxy statement)
            throws SQLException {
        String sqlCharacter = getSqlCharacter(statement.getSql());

        Transaction transaction = Cat.newTransaction(CatConstants.TYPE_SQL, sqlCharacter);
        Cat.logEvent("SQL.DbType", dbType);
        Cat.logEvent("SQL.Database", url);
        Cat.logEvent("SQL.param", getParams(statement.getParameters()));
        //Cat.logEvent("SQL.sql", getShowSql(sqlCharacter,getParams(statement.getParameters())));
        int result;
        try {
            result = super.preparedStatement_executeUpdate(chain, statement);
            transaction.setStatus(Message.SUCCESS);
        } catch (SQLException e) {
            Cat.logError(e);
            transaction.setStatus(e);
            throw e;
        } finally {
            transaction.complete();
        }
        return result;
    }

    private String getSqlType(String sql) {

        String character = "NULL";
        if (Objects.isNull(sql)) {
            return character;
        }
        try {
            String sqlToLowerCase = sql.toLowerCase();
            if (sqlToLowerCase.startsWith("update") || sqlToLowerCase.contains("delete")) {
                return "UPDATE";
            } else if (sqlToLowerCase.startsWith("insert")) {
                return "INSERT";
            } else if (sqlToLowerCase.startsWith("delete")) {
                return "DELETE";
            } else if (sqlToLowerCase.startsWith("select")) {
                return "SELECT";
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return character;
    }

    private String getSqlCharacter(String sql) {

        //String character = "NULL";
        //if (Objects.isNull(sql)) {
        //    return character;
        //}
        //
        //try {
        //    String sqlToLowerCase = sql.toLowerCase();
        //    String[] segs = sqlToLowerCase.split(" ");
        //    if (sqlToLowerCase.contains("update") || sqlToLowerCase.contains("delete")) {
        //        if (segs.length > 2) {
        //            character = new StringBuffer(segs[0]).append(" ").append(segs[1]).toString();
        //        }
        //        character = segs[0];
        //
        //    } else if (sqlToLowerCase.contains("insert")) {
        //        if (segs.length > 2) {
        //            character = new StringBuffer(segs[0]).append(" ").append(segs[2]).toString();
        //        }
        //        character = segs[0];
        //    } else if (sqlToLowerCase.contains("select")) {
        //        character = "";
        //    }
        //} catch (Exception e) {
        //    e.printStackTrace();
        //}
        //return character;
        //TODO 观察sql的基数是否一直可控，避免内存溢出
        return sql;
    }

    private String getParams(Map<Integer, JdbcParameter> parameters) {
        if (parameters == null || parameters.isEmpty()) {
            return "";
        }

        StringBuilder paramsBuilder = new StringBuilder("");
        parameters.forEach(new BiConsumer<Integer, JdbcParameter>() {
            @Override
            public void accept(Integer integer, JdbcParameter jdbcParameter) {
                paramsBuilder.append(jdbcParameter.getValue()).append(",");
            }
        });

        return paramsBuilder.toString();
    }

    private String getShowSql(String sqlCharacter, String params) {
        return new StringBuilder(sqlCharacter).append("\r\n").append(params).toString();
    }
}
